<?php

/**
 * RequestController
 *
 * @author yukio
 * @version $Id$
 */

require_once 'Zend/Controller/Action.php';
require_once 'Okute/Util/Data.php';
require_once 'Okute/Exception.php';

require_once APPLICATION_PATH . '/models/RequesterModel.php';
require_once APPLICATION_PATH . '/models/RequestModel.php';
require_once APPLICATION_PATH . '/models/ShelterModel.php';
require_once APPLICATION_PATH . '/models/SupportModel.php';
require_once APPLICATION_PATH . '/models/StatusModel.php';
require_once APPLICATION_PATH . '/models/AclModel.php';

define ('DEFAULT_SEVERITY', 1);

class RequestController extends Zend_Controller_Action
{
    /**
     * 要求モデルのインスタンス
     * @var RequestModel
     */
    private $_requestModel;

    /**
     * セッション管理用変数
     * @var Zend_Session_Namespace
     */
    private $_session;

    /**
     * 共通関数用
     * @var Okute_Util_Data
     */
    private $_util;

    /**
     * アクセスコントロール
     * @var Zend_Acl
     */
    private $_acl;
    
    /**
     * リダイレクトヘルパー
     * @var Okute_Controller_Action_Helper_Redirector
     */
    private $_redirector;

    /**
     * Requestコントローラの初期化
     *
     * @return void
     */
    public function init() {
        $this->_session = new Zend_Session_Namespace('okute');
        $this->_util = new Okute_Util_Data($this->view);
        $this->_acl = Zend_Registry::get('acl');
        $this->_redirector = Zend_Registry::get('redirector');
        $this->view->base = $this->getRequest()->getBaseUrl();
        
        if ($this->_session->requester !== null) {        
	        $this->view->user = $this->_session->requester->name;
			$this->view->role = AclModel::ROLE_REQUESTER;
        }
		
        if ($this->_session->shelter === null &&
            $this->_session->role !== AclModel::ROLE_OPERATOR ) {
            $this->_redirector->redirectWithSession($this->view->base.'/shelter/list');
        }
        
        $this->_requestModel = new RequestModel();
    }

    /**
     * indexActionは、請求画面のメニューを表示する前処理として、セッションクリアを行う。
     *
     * @return void
     */
    public function indexAction() {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUESTER_MENU)) {
            throw new Okute_Exception('Access Control Error: role is ' . $this->_session->role);
        }
        $this->_util->clearSessionData();
        $this->view->requester_id = $this->_session->requester->requester_id;
        $this->view->shelter_id   = $this->_session->requester->shelter_id;
        if ($this->_session->format === 'pc') {
            $this->_forward('index', 'list');
        }
    }

    /**
     * createActionは新規請求画面の前処理を行う。
     * 実際の処理はeditActionで行う。
     *
     * @return void
     */
    public function createAction() {
        $this->_util->clearSessionData();
        $this->_setParam('new', true);
        $this->_forward('edit');
    }

    /**
     * editActionは請求の編集を行うための前処理を実施する。編集結果はセッションに配列型で保存する。
     * 新規作成画面から遷移してきた場合には、セッションデータがないため、新たにデータを作成する。
     * 閲覧画面から詳細の編集で遷移してきた場合は、セッションデータがないため、データベースから読み込む。
     * 確認画面から戻ってきた場合には、セッションデータをそのまま使用する。
     * 入力エラーにより強制的に戻された場合には、ユーザ入力データが存在するため、セッションデータとマージする。
     *
     * @return void
     */
    public function editAction() {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_EDIT)) {
            throw new Okute_Exception('Access Control Error');
        }
                
        $data = null;

        // セッションデータがない場合は「メニューの新規」「詳細の編集」から遷移
        if ($this->_session->userData === null) {
            // 「メニュー新規」から遷移
            if ($this->_getParam('new', false) === true) {
                $columns = array ('requester_id' => $this->_session->requester->requester_id,
                                  'addressee_shelter_id' => $this->_session->requester->shelter_id,
                                  'severity' => DEFAULT_SEVERITY);
                $data = $this->_requestModel->createRow($columns);
            }
            // 「詳細の編集」から遷移
            else if ($this->_session->reqId !== null) {
                $select = $this->_requestModel->select();
                $select->where('req_id = ?', $this->_session->reqId);
                $data = $this->_requestModel->fetchRow($select);
            }
            // どちらでもなければエラー
            else {
                throw new Okute_Exception('Invalid page-transition');
            }
        }
        // セッションデータがある場合は確認画面からの戻るボタンによる遷移
        else {
            $data = $this->_requestModel->createRow($this->_session->userData);
        }

        // ポストデータがある場合は入力エラーからの戻り
        if ($this->getRequest()->getPost('req_id', null) !== null) {
            $data = $this->_util->postToData($request, $data);
            $data->requester_id = $this->_session->requester->requester_id;
            $data->addressee_shelter_id = $this->_session->shelter->shelter_id;
        }

        // 編集結果の保存
        $this->_util->dataToSession($data);

        if ($this->_session->displayId !== null) {
            $this->view->back = $this->view->base.'/list/detail/id/' . $this->_session->displayId;
        }
        $this->_util->dataToView($data);
        $this->view->shelter = $this->_session->shelter;
    }

    /**
     * deleteActionはセッション内に保持された表示IDから削除する請求IDを取得し、
     * 請求の削除を行う。
     *
     * @return void
     */
    public function deleteAction() {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_DELETE)) {
            throw new Okute_Exception('Access Control Error');
        }

        $map = $this->_session->requestMap;
        if ($map === null) {
            throw new Okute_Exception('Session data "requestMap" is required');
        }
        $displayId = $this->getRequest()->getParam('id');
        if ($displayId === null) {
            throw new Okute_Exception('Session data "displayId" is requred');
        }
        $reqId = $map[$displayId];
        if ($reqId === null) {
            throw new Okute_Exception('No valid "req_id" in "requestMap"');
        }

        $this->_requestModel->delete(array('req_id' => $reqId));
    }

    /**
     * enableActionは請求のenable/disableを切り替える。
     *
     * @return void
     */
    public function enableAction() {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_DISABLE_REQUEST)) {
            throw new Okute_Exception('Access Control Error');
        }
        
        $request = $this->getRequest();
        $id = $request->getParam('id');
        $enable = $request->getParam('enable');
        
        $db = $this->_requestModel->getAdapter();
        $where = array(
            $db->quoteInto('req_id = ?', $id),
        );
        $this->_requestModel->update(array('enable' => $enable), $where);
        $this->_redirector->redirectWithSession($this->view->base.'/list/');
    }
    
    /**
     * confirmActionは、確認画面に遷移するための前処理を行う。
     * 主な前処理はユーザ入力項目のチェックである。
     *
     * @return void
     */
    public function confirmAction() {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_EDIT)) {
            throw new Okute_Exception('Access Control Error');
        }
        if ($this->_session->userData === null) {
            throw new Okute_Exception('Session data "userData" is required');
        }
        if ($this->_session->shelter->shelter_id === null) {
            throw new Okute_Exception('Session data "shelter" is required');
        }

        $data = $this->_requestModel->createRow($this->_session->userData);
        $data = $this->_util->postToData($this->getRequest(),$data);
        $data->requester_id = $this->_session->requester->requester_id;
        $data->addressee_shelter_id = $this->_session->shelter->shelter_id;

        $errors = $this->_checkInput($data);
        if (count($errors) > 0) {
            $this->view->errors = $errors;
            $this->_forward('edit');
        } else {
            $this->view->errors = null;
        }

        $this->_util->dataToSession($data);
        $this->_util->dataToView($data);
        $this->view->shelter = $this->_session->shelter;
        $this->_session->confirm = true;
    }

    /**
     * finishActionではセッションに保存されたデータをデータベースに書きこむ。
     * finishActionは必ず確認画面を通した後実施される。
     *
     * @return void
     */
    public function finishAction() {
        if (!$this->_acl->isAllowed($this->_session->role, AclModel::RES_REQUEST_EDIT)) {
            throw new Okute_Exception('Access Control Error');
        }
        if ($this->_session->confirm === null) {
            throw new Okute_Exception('Invalid page-transition');
        }
        $this->_session->confirm = null;

        $request = $this->getRequest();
        if ($this->_session->userData !== null) {
            $data = $this->_requestModel->createRow($this->_session->userData);
            $data->req_id = $this->_session->reqId;

            if ($data->create_datetime === null) {
                $data->create_datetime = 'NOW()';
            }
            $data->update_datetime = 'NOW()';

            $db = Zend_Registry::get('database', null);
            if ($db === null) {
                throw new Okute_Exception('Invalid registry "database"');
            }

            if ($this->_session->reqId === null) {
                $db->beginTransaction();
                try {
                    $data->enable = 1;
                    $data->save();

                    $reqId = $db->lastInsertId('request_info', 'req_id');
                    $supportModel = new SupportModel();
                    $support = $supportModel->createRow(
                        array('req_id' => $reqId,
                              'last_status' => StatusModel::REQUEST_WAIT,
                              'create_datetime' => 'NOW()',
                              'update_datetime' => 'NOW()'));
                            $support->save();

                            $statusModel = new StatusModel();
                            $status = $statusModel->createRow(
                                array('req_id' => $reqId,
                                      'update_datetime' => 'NOW()',
                                      'status' => StatusModel::REQUEST_WAIT,
                                      'update_id' => $this->_session->requester->requester_id));
                            $status->save();

                            $db->commit();
                            $this->_util->clearSessionData();
                } catch (Exception $e) {
                    $db->rollBack();
                    throw $e;
                }
            } else {
                $where = $this->_requestModel->getAdapter()
                    ->quoteInto('req_id = ?', $this->_session->reqId);
                $this->_requestModel->update($data->toArray(), $where);

                $this->_redirector->redirectWithSession(
                	$this->view->base.'/list/detail/id/'.$this->_session->displayId);
            }
        }
        if ($this->_session->format === 'pc') {
            $this->_forward('index', 'list');
        }
    }

    /**
     * ユーザ入力のエラーを確認する。エラーが存在した場合には配列にエラーコードとメッセージを含めて返却する。
     * 引数にはRequestModelのデータを入力する。
     *
     * @param Zend_Db_Table_Row $data
     * @return array $errors
     */
    private function _checkInput($data) {
        $errors = array();
        if ($data->req_content === '') {
            $errors[] = array('code' => '1000', 'message' => '必要な物資を入力してください');
        }

        if ($data->req_amount === '') {
            $errors[] = array('code' => '1001', 'message' => '数量を入力してください');
        }
        
        // 支援物資は128文字以下
        if (strlen($data->req_content) > 128) {
            $errors[] = array('code' => '1002', 'message' => '必要な物資は128文字以下で入力してください');
        }
        
        // 数量は128文字以下
        if (strlen($data->req_amount) > 128) {
            $errors[] = array('code' => '1003', 'message' => '数量は128文字以下で入力してください');
        }
        
        // 届け先付加情報は256文字以下
        if (strlen($data->addressee_notes) > 256) {
            $errors[] = array('code' => '1004', 'message' => '届け先付加情報は256文字以下で入力してください');
        }
        
        // 緊急度は数値の0〜4
        if ($data->severity < 0 || $data->severity > 4) {
            $errors[] = array('code' => '1005', 'message' => '緊急度に不正な値が含まれています');
        }
        
        // コメントは256文字以下
        if (strlen($data->notes) > 256) {
            $errors[] = array('code' => '1006', 'message' => 'コメントは256文字以下で入力してください');
        }
        
        return $errors;
    }

}
